/*
 *
 * System2 Simulation Framework
 * Copyright (C) 2011 - 2012 Jacob Dawid
 * jacob.dawid@googlemail.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "simulator.h"

namespace System2 {
    Simulator::Simulator()
        : QThread() {
        m_simulationQueueSemaphore = new QSemaphore(1);
        m_haltedSemaphore = new QSemaphore(1);
        m_server = new Server(this);
        m_client = new Client(this);
        m_halted = false;
        m_jobIdIterator = 0;
        start();
    }

    Simulator::~Simulator() {
        delete m_simulationQueueSemaphore;
        delete m_haltedSemaphore;
    }

    void Simulator::process(Simulation *simulation) {
        SemaphoreScopeLocker locker(m_simulationQueueSemaphore);
        (void)locker; // Avoid unused warnings.
        m_simulationQueue.push_back(simulation);
    }

    bool Simulator::hasBeenHalted() {
        SemaphoreScopeLocker locker(m_haltedSemaphore);
        (void)locker; // Avoid unused warnings.
        return m_halted;
    }

    void Simulator::halt() {
        {
            SemaphoreScopeLocker locker(m_haltedSemaphore);
            (void)locker; // Avoid unused warnings.
            m_halted = true;
        }

        while(!isFinished()) {
            QThread::msleep(250);
        }
    }

    bool Simulator::simulationsRemaining() {
        SemaphoreScopeLocker locker(m_simulationQueueSemaphore);
        (void)locker; // Avoid unused warnings.
        return m_simulationQueue.size() > 0 ? true : false;
    }

    Simulation *Simulator::nextSimulation() {
        SemaphoreScopeLocker locker(m_simulationQueueSemaphore);
        (void)locker; // Avoid unused warnings.
        if(!m_simulationQueue.isEmpty()) {
            Simulation *simulation = m_simulationQueue.first();
            m_simulationQueue.removeFirst();
            return simulation;
        }
        return 0;
    }

    void Simulator::run() {
        while(!hasBeenHalted() || simulationsRemaining() || jobsRemaining()) {
            // As long as simulations remain, just process the
            // next one in the queue.
            while(simulationsRemaining()) {
                Simulation *simulation = nextSimulation();
                if(simulation) {
                    m_remainingJobs.append(simulation->atomize(m_jobIdIterator));
                }
            }

            workOnJobs();

            QThread::msleep(250);
        }
    }

    void Simulator::workOnJobs() {
        foreach(Atom* job, m_remainingJobs) {
            switch(job->state()) {

            case Atom::Waiting:
                if(job->isIndependent()) {
                    job->setSignalBundle(new SignalBundle());
                    job->start();
                }
                break;

            case Atom::Finished:
                foreach(Atom* followUp, m_remainingJobs) {
                    if(followUp->isDependentOf(job->id())) {
                        followUp->setSignalBundle(new SignalBundle(*job->signalBundle()));
                        followUp->start();
                    }
                }
                m_remainingJobs.removeAll(job);
                m_depositedJobs.append(job);
                job->deposit();                
                break;

            case Atom::Running:
            case Atom::Deposited:
                break;
            }
        }
    }

    bool Simulator::jobsRemaining() {
        return m_remainingJobs.size() > 0 ? true : false;
    }
}
